#include "http.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <curl/curl.h>
#include "curl/multi.h"
#include <algorithm>
uv_loop_t *loop=nullptr;

CURLM *curl_handle=nullptr;
uv_timer_t timeout;

//
char *getFileNameByUrl(const char *url, const char *fieldname = nullptr)
{

    int begin = 0, end = 0, pos = (int)url;
    boolean filename_begin = false;

    while (1)
    {

        char c = *((char *)pos);
        end = pos - 1;
        if (c == '\0')
        {

            break;
        }
        else if (c == '/')
        {
            begin = pos + 1;
        }
        else if (c == '?')
        {

            if (fieldname == nullptr)
            {
                break;
            }
        }

        if (fieldname != nullptr)
        {

            int fieldlen = strlen(fieldname);
            if (strncmp((char *)pos, fieldname, fieldlen) == 0 && *((char *)(pos + fieldlen)) == '=')
            {

                begin = pos + fieldlen + 1;
                filename_begin = true;
            }

            if (filename_begin)
            {
                if (c == '&')
                {
                    end = pos - 1;
                    break;
                }
                else
                {

                    end = pos;
                }
            }
        }
        ++pos;
    }

    if (end >= begin)
    {

        char *file = (char *)malloc(end - begin + 2);
        memset(file, 0, end - begin + 2);
        strncpy(file, (char *)begin, end - begin + 1);

        return file;
    }

    return nullptr;
}

FILE *log1 = fopen("log.txt", "w+");

void addDownload2(CURLM *curl_handle, const char *url,const char *fileName)
{

    CURL *curl = curl_easy_init();

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
    // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5000);
    //curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 2000);
    //curl_easy_setopt(curl,CURLOPT_NOSIGNAL,1);


    FILE *f = fopen(fileName, "wb+");


    curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(curl, CURLOPT_PRIVATE, f);

    if (strncmp(url, "https", 5) == 0)
    {

        printf("%s\n", url);
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    }

    if (CURLM_OK != curl_multi_add_handle(curl_handle, curl))
    {

        // MessageBoxA(0, "-----------", "", 0);
    }
}

void addDownload(CURLM *curl_handle, const char *url)
{

    CURL *curl = curl_easy_init();

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
    // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5000);
    //curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 2000);
    //curl_easy_setopt(curl,CURLOPT_NOSIGNAL,1);

    const char *filename_pos = strstr(url, "filename");

    char *filename = nullptr;

    if (filename_pos == nullptr)
    {
        filename = getFileNameByUrl(url);
    }
    else
    {
        filename = getFileNameByUrl(url, "filename");
    }

    FILE *f = fopen(filename, "wb+");

    if (filename)
    {
        free(filename);
    }

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(curl, CURLOPT_PRIVATE, f);

    if (strncmp(url, "https", 5) == 0)
    {

        printf("%s\n", url);
        curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    }

    if (CURLM_OK != curl_multi_add_handle(curl_handle, curl))
    {

        // MessageBoxA(0, "-----------", "", 0);
    }
}

int addDownloads(CURLM *curl_handle, char *urls)
{

    int count = 0;

    char *context;

    char *p = strtok_s(urls, ",", &context);

    while (p != nullptr)
    {
        fwrite(p, 1, strlen(p), log1);

        fwrite((const char *)"\n", 1, 1, log1);
        count++;
        addDownload(curl_handle, p);
        p = strtok_s(nullptr, ",", &context);
    }

    return count;
}

static void check_multi_info()
{

    CURLMsg *message;
    int pending;
    CURL *easy_handle;

    FILE *f;

    while ((message = curl_multi_info_read(curl_handle, &pending)))
    {
        switch (message->msg)
        {
        case CURLMSG_DONE:

            easy_handle = message->easy_handle;

            curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &f);
            curl_multi_remove_handle(curl_handle, easy_handle);
            curl_easy_cleanup(easy_handle);
            if (f)
            {
                fclose(f);
            }
            break;

        default:
            break;
        }
    }
}

typedef struct curl_context_s
{
    uv_poll_t poll_handle;
    curl_socket_t sockfd;
} curl_context_t;

static curl_context_t *create_curl_context(curl_socket_t sockfd)
{
    curl_context_t *context;

    context = (curl_context_t *)malloc(sizeof(curl_context_t));

    context->sockfd = sockfd;

    uv_poll_init_socket(loop, &context->poll_handle, sockfd);
    context->poll_handle.data = context;

    return context;
}

static void curl_close_cb(uv_handle_t *handle)
{
    curl_context_t *context = (curl_context_t *)handle->data;
    free(context);
}

static void destroy_curl_context(curl_context_t *context)
{
    uv_close((uv_handle_t *)&context->poll_handle, curl_close_cb);
}

static void curl_perform(uv_poll_t *req, int status, int events)
{
    int running_handles;
    int flags = 0;
    curl_context_t *context;

    if (events & UV_READABLE)
        flags |= CURL_CSELECT_IN;
    if (events & UV_WRITABLE)
        flags |= CURL_CSELECT_OUT;

    context = (curl_context_t *)req->data;

    curl_multi_socket_action(curl_handle, context->sockfd, flags,
                             &running_handles);

    check_multi_info();
}

static void on_timeout(uv_timer_t *req)
{
    int running_handles;
    curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
                             &running_handles);
    check_multi_info();
}

static int start_timeout(CURLM *multi, long timeout_ms, void *userp)
{
    if (timeout_ms < 0)
    {
        uv_timer_stop(&timeout);
    }
    else
    {
        if (timeout_ms == 0)
            timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it
                               in a bit */
        uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
    }
    return 0;
}

static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
                         void *socketp)
{
    curl_context_t *curl_context;
    int events = 0;

    switch (action)
    {
    case CURL_POLL_IN:
    case CURL_POLL_OUT:
    case CURL_POLL_INOUT:
        curl_context = socketp ? (curl_context_t *)socketp : create_curl_context(s);

        curl_multi_assign(curl_handle, s, (void *)curl_context);

        if (action != CURL_POLL_IN)
            events |= UV_WRITABLE;
        if (action != CURL_POLL_OUT)
            events |= UV_READABLE;

        uv_poll_start(&curl_context->poll_handle, events, curl_perform);
        break;
    case CURL_POLL_REMOVE:
        if (socketp)
        {
            uv_poll_stop(&((curl_context_t *)socketp)->poll_handle);
            destroy_curl_context((curl_context_t *)socketp);
            curl_multi_assign(curl_handle, s, NULL);
        }
        break;
    default:
        abort();
    }

    return 0;
}

void dhs_downloadFile(rapidjson::Value & v)
{
    const char * imagePath=getImagePath();
     
     chdir(imagePath);

    if (curl_global_init(CURL_GLOBAL_ALL))
    {
        fprintf(stderr, "Could not init curl\n");
    }

    loop = uv_default_loop();
    uv_timer_init(loop, &timeout);

    curl_handle = curl_multi_init();

    curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
    curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
    
    auto urlList=  v.GetArray();

    // for (rapidjson::Value::ConstValueIterator it = urlList.Begin(); it != urlList.End(); it++){
         
    //      if(it->IsObject()){
             
    //      const char* fileName=  it->GetObj()["fileName"].GetString();
    //      const char* fileUrl=  it->GetObj()["fileUrl"].GetString();
             
    //   addDownload2(curl_handle, fileUrl,fileName);
    //      }

    // }

    
     std::for_each(urlList.begin(),urlList.end(),[](rapidjson::Value &  item){

   

  const char* fileName=  item.GetObj()["fileName"].GetString();
         const char* fileUrl=  item.GetObj()["fileUrl"].GetString();
             
      addDownload2(curl_handle, fileUrl,fileName);

     });

    uv_run(loop, UV_RUN_DEFAULT);

    curl_multi_cleanup(curl_handle);
    curl_global_cleanup();

    fclose(log1);
}

void start(char *url)
{

    if (curl_global_init(CURL_GLOBAL_ALL))
    {
        fprintf(stderr, "Could not init curl\n");
    }

    loop = uv_default_loop();
    uv_timer_init(loop, &timeout);

    curl_handle = curl_multi_init();

    curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
    curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);

    int count = addDownloads(curl_handle, url);

    uv_run(loop, UV_RUN_DEFAULT);

    curl_multi_cleanup(curl_handle);
    curl_global_cleanup();

    fclose(log1);
}

const char *  getImagePath(){
static  char imagepath[500]={'\0'};

 if(strlen(imagepath)>0){
    return imagepath;
 }

    GetCurrentDirectoryA(500,imagepath);
    strcat(imagepath,"\\image\\");
    mkdir(imagepath);

return imagepath;

}

void downloadfile(const char * url_list){
   
// SHELLEXECUTEINFOA shi;
// shi.cbSize=sizeof(SHELLEXECUTEINFOA);
// shi.lpVerb="open";
// shi.lpFile=commandpath;
// shi.lpParameters=url;
// shi.nShow=SW_HIDE;
// shi.fMask=SEE_MASK_NOCLOSEPROCESS;
// ShellExecuteExA(&shi);

// WaitForSingleObject(shi.hProcess,INFINITE);
// if( shi.hProcess != NULL)
// {
//       TerminateProcess(shi.hProcess,0);     
// }

const char * imagePath=getImagePath();
     
     chdir(imagePath);
     start((char*)url_list);

}